/////////////////////////////////////////////     File     //////////////////////////////////////////////////
// Author: James Chen
// Filename: Input.cpp
// Created: Tuesday, August 10, 2010 3:28:24 PM
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "./CInput.h"
#include <stdio.h>

//	Disable "signed/unsigned mismatch" warning.
#pragma warning (disable : 4018)

////////////////////// Singleton Implementation Begins //////////////////////
CInput *CInput::m_pInstance = 0;

CInput::CInput(void)
{
	m_lpDIObject		= NULL;
	m_lpDIKey			= NULL;
	m_lpDIMouse			= NULL;
	m_bExclusive		= false;
	m_dwKeyElements		= 0;
	m_dwMouseElements	= 0;
	memset(&m_didodMouse, 0, sizeof(m_didodMouse));
	memset(&m_didodKey, 0, sizeof(m_didodKey));
}

CInput::~CInput(void)
{
}

CInput *CInput::GetInstance(void)
{
	if(!m_pInstance)
	{
		m_pInstance = new CInput;
	}

	return m_pInstance;
}

void CInput::DeleteInstance(void)
{
	if(m_pInstance)
	{
		delete m_pInstance;
		m_pInstance = 0;
	}
}
/////////////////////// Singleton Implementation Ends ///////////////////////

bool CInput::InitDevices(HWND hWnd, HINSTANCE hMainInstance, bool bExclusive)
{
	//	Make sure DirectInput hasn't already been initialized.
	//if (m_lpDIObject)	DIERRBOX("DirectInput has already been initialized");

	m_hWnd		 = hWnd;			//	Store Main Window Handle.
	m_hInstance  = hMainInstance;	//	Store the main application instance.
	m_bExclusive = bExclusive;		//	Store whether or not we want exclusive access of the device.

	if (!InitKeyboard(hWnd, hMainInstance, bExclusive))
	{
		DIERRBOX("Failed to Initialize the Keyboard");
	}

	if (!InitMouse(hWnd, hMainInstance, bExclusive))
	{
		DIERRBOX("Failed to Initialize the Mouse");
	}

	//	Return success.
	return true;
}


bool CInput::InitKeyboard(HWND hWnd, HINSTANCE hMainInstance, bool bExclusive)
{
	//	Make sure Keyboard hasn't already been initialized.
	//if (m_lpDIKey)	DIERRBOX("DirectInput Keyboard has already been initialized");

	m_hWnd		 = hWnd;			//	Store Main Window Handle.
	m_hInstance  = hMainInstance;	//	Store the main application instance.
	m_bExclusive = bExclusive;		//	Store whether or not we want exclusive access of the device.

	//	Only create the Main DirectInput object if it hasn't been created yet.
	if (!m_lpDIObject)
	{
		//	Initialize the Direct Input COM object.
		if (FAILED(DirectInput8Create(hMainInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&m_lpDIObject, NULL)))
		{
			DIERRBOX("Failed to Create Main DirectInput Object");
		}
	}

	//	Create the Keyboard Device.
	if (FAILED(m_lpDIObject->CreateDevice(GUID_SysKeyboard, &m_lpDIKey, NULL)))
	{
		DIERRBOX("No Keyboard Found.");
	}

	//	Set the Cooperative level for the keyboard.
	if (m_bExclusive)
	{
		if (FAILED(m_lpDIKey->SetCooperativeLevel(hWnd, DISCL_FOREGROUND | DISCL_EXCLUSIVE)))
		{
			DIERRBOX("Failed to SetCooperativeLevel of Keyboard for Exclusive Mode.");
		}
	}
	else
	{
		if (FAILED(m_lpDIKey->SetCooperativeLevel(hWnd, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE)))
		{
			DIERRBOX("Failed to SetCooperativeLevel of Keyboard.");
		}
	}

	//	Set the Data Format for the keyboard.
	if (FAILED(m_lpDIKey->SetDataFormat(&c_dfDIKeyboard)))
	{
		DIERRBOX("Failed to SetDataFormat for the Keyboard.");
	}

	//	For Use With Buffered Input.
	DIPROPDWORD dipdw;

	memset(&dipdw, 0, sizeof(dipdw));
	dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
	dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
	dipdw.diph.dwObj        = 0;
	dipdw.diph.dwHow        = DIPH_DEVICE;
	dipdw.dwData            = BUFFER_SIZE;	//	Arbitrary buffer size

	if (FAILED(m_lpDIKey->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph)))
	{
		DIERRBOX("Could not Set the Properties for Buffered Input for Keyboard.");
	}

	//	Acquire the Keyboard.
	if (FAILED(m_lpDIKey->Acquire()))
	{
		DIERRBOX("Failed to Acquire Keyboard.");
	}

	//	Gets the Keyboard Layout.
	m_keyLayout = GetKeyboardLayout(0);

	//	Return success.
	return true;
}


bool CInput::InitMouse(HWND hWnd, HINSTANCE hMainInstance, bool bExclusive)
{
	//	Make sure the Mouse hasn't already been initialized.
	if (m_lpDIMouse)
	{
		DIERRBOX("DirectInput Mouse has already been Initialized.");
	}

	m_hWnd		 = hWnd;			//	Store Main Window Handle.
	m_hInstance  = hMainInstance;	//	Store the main application instance.
	m_bExclusive = bExclusive;		//	Store whether or not we want exclusive access of the device.

	//	Only create the Main DirectInput object if it hasn't been created yet.
	if (!m_lpDIObject)
	{
		//	Initialize the Direct Input COM object.
		if (FAILED(DirectInput8Create(hMainInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&m_lpDIObject, NULL)))
		{
			DIERRBOX("Failed to Create the DirectInput Object.");
		}
	}

	//	Create the Mouse Device.
	if (FAILED(m_lpDIObject->CreateDevice(GUID_SysMouse, &m_lpDIMouse, NULL)))
	{
		DIERRBOX("Mouse not Found.");
	}

	//	Set the Cooperative Level for the Mouse.
	if (m_bExclusive)
	{
		if (FAILED(m_lpDIMouse->SetCooperativeLevel(hWnd, DISCL_FOREGROUND | DISCL_EXCLUSIVE)))
		{
			DIERRBOX("Failed to SetCooperativeLevel for the Mouse in Exclusive Mode.");
		}
	}
	else
	{
		if (FAILED(m_lpDIMouse->SetCooperativeLevel(hWnd, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE)))
		{
			DIERRBOX("Failed to SetCooperativeLevel for the Mouse.");
		}
	}

	//	Set the Data format for the mouse.
	if (FAILED(m_lpDIMouse->SetDataFormat(&c_dfDIMouse2)))
	{
		DIERRBOX("Failed to SetDataFormat for the Mouse.");
	}

	//	For Use With Buffered Input.
	DIPROPDWORD dipdw;

	memset(&dipdw, 0, sizeof(dipdw));
	dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
	dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
	dipdw.diph.dwObj        = 0;
	dipdw.diph.dwHow        = DIPH_DEVICE;
	dipdw.dwData            = BUFFER_SIZE;	//	Arbitrary buffer size

	if (FAILED(m_lpDIMouse->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph)))
	{
		DIERRBOX("Could not Set the Properties for Buffered Input for Mouse.");
	}

	//	Acquire the mouse.
	if (FAILED(m_lpDIMouse->Acquire()))
	{
		DIERRBOX("Failed to Acquire the Mouse.");
	}

	//	Return success.
	return true;
}

void CInput::ShutdownDirectInput(void)
{
	//	Release the Mouse.
	if (m_lpDIMouse)
	{
		m_lpDIMouse->Unacquire();
		SAFE_RELEASE(m_lpDIMouse);
	}

	//	Release the Keyboard
	if (m_lpDIKey)
	{
		m_lpDIKey->Unacquire();
		SAFE_RELEASE(m_lpDIKey);
	}

	//	Release the Direct Input Device.
	if (m_lpDIObject)
	{
		SAFE_RELEASE(m_lpDIObject);
	}
}

bool CInput::ReadDevices(void)
{
	if (!ReadKeyboard())
		return false;

	if (!ReadBufferedKeyboard())
		return false;

	if (!ReadMouse())
		return false;

	if (!ReadBufferedMouse())
		return false;

	return true;
}


bool CInput::ReadKeyboard(void)
{
	HRESULT result;	//	For Error Tracking.

	//	Make sure the keyboard device was made.
	if (!m_lpDIKey)		return false;

	memset(&m_ucKeyState, 0, sizeof(m_ucKeyState));

	m_lpDIKey->Acquire();

	//	Keep attempting to acquire the Keyboard.
	if (result = m_lpDIKey->GetDeviceState(sizeof(m_ucKeyState), (LPVOID)m_ucKeyState) == DIERR_INPUTLOST)	   
	{
		//	Try to re-acquire the device.
		if (FAILED(result = m_lpDIKey->Acquire()))
		{
			//	Could not re-acquire the Keyboard.
			return false;
		}
	}

	//	Return success.
	return true;
}

bool CInput::ReadBufferedKeyboard(void)
{
	HRESULT result;	//	For Error Tracking.

	//	Make sure the keyboard device was made.
	if (!m_lpDIKey)		return false;

	m_lpDIKey->Acquire();

	m_dwKeyElements = BUFFER_SIZE;
	memset(&m_didodKey, 0, sizeof(m_didodKey));
	//	Keep attempting to acquire the Keyboard.
	if (result = m_lpDIKey->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), m_didodKey, &m_dwKeyElements, 0) == DIERR_INPUTLOST)
	{
		//	Could not re-acquire the Keyboard.
		if (FAILED(result = m_lpDIKey->Acquire()))
			return false;
	}

	//	Return success.
	return true;
}


bool CInput::GetKey(UCHAR ucKey)
{
	//	Check to see if the key was pressed.
	if (m_ucKeyState[ucKey] & 0x80)
		return true;

	//	The Key was not Pressed.
	return false;
}

//	Disable the "not all control paths return a value" warning.
#pragma warning (disable : 4715)


char CInput::CheckKeys(void)
{	
	//	Get the Current Keyboard State.
	if (!(GetKeyboardState(m_ucAsciiVals)))
	{
		DIERRBOX("Could not GetKeyboardState.");
	}

	unsigned short num = 0;
	UINT vkcode = 0;

	//	Go through each keyboard state.
	for (int i = 0; i < 256; i++)
	{
		if (m_ucKeyState[i] & 0x80)
		{
			//	Map the Scan Code from Direct Input to a Virtual Key value....
			vkcode = MapVirtualKeyEx(i, 1, m_keyLayout);
			//	...and tranlsate that Virtual Key into an Ascii Value.
			ToAsciiEx(vkcode, i, m_ucAsciiVals, &num, 0, m_keyLayout);
		}
	}

	//	Return the Ascii Value.
	return (char)num;
}

char CInput::CheckBufferedKeys(void)
{
	//	Get the Current Keyboard State.
	if (!(GetKeyboardState(m_ucAsciiVals)))
	{
		DIERRBOX("Could not GetKeyboardState.");
	}

	unsigned short num = 0;
	UINT vkcode = 0;

	//	Go through each element in the Buffer.
	for (DWORD i = 0; i < m_dwKeyElements; i++)
	{
		for (unsigned int j = 0; j < 256; j++)
		{
			if ((m_didodKey[i].dwOfs == j) && (m_didodKey[i].dwData & 0x80))
			{
				vkcode = MapVirtualKeyEx(m_didodKey[i].dwOfs, 1, m_keyLayout);
				ToAsciiEx(vkcode, m_didodKey[i].dwOfs, m_ucAsciiVals, &num, 0, m_keyLayout);
			}
		}
	}

	//	Return the Ascii Value.
	return (char)num;	
}

//	Enable the "not all control paths return a value" warning.
#pragma warning (default : 4715)


bool CInput::GetBufferedKey(UCHAR ucKey)
{
	//	Go through each element in the Buffer.
	for (DWORD i = 0; i < m_dwKeyElements; i++)
	{
		//	If the Key is pressed return success.
		if ((m_didodKey[i].dwOfs == ucKey) && (m_didodKey[i].dwData & 0x80))
			return true;
	}

	//	Return Failure.
	return false;
}


bool CInput::GetBufferedKeyEx(UCHAR ucKey)
{
	if (GetKey(ucKey))
	{
		if (!m_bKeysPressed[ucKey])
		{
			m_bKeysPressed[ucKey] = true;
			return true;
		}
	}
	else if (!GetKey(ucKey))
		m_bKeysPressed[ucKey] = false;

	//	The Key was not pressed.
	return false;
}


bool CInput::ReadMouse(void)
{
	HRESULT result;	//	For Error Tracking.

	//	Make sure the Mouse Device was made.
	if (!m_lpDIMouse)	return false;

	if (FAILED(m_lpDIMouse->Poll()))
		m_lpDIMouse->Acquire();

	memset(&m_diMouseState, 0, sizeof(m_diMouseState));
	//	Keep attempting to acquire the Mouse.
	if (result = m_lpDIMouse->GetDeviceState(sizeof(m_diMouseState), &m_diMouseState) == DIERR_INPUTLOST)
	{
		//	Try to re-acquire the device.
		if (FAILED(result = m_lpDIMouse->Acquire()))
			//	Serious error - Could not re-acquire the Mouse.
			return false;
	}

	//	Return success.
	return true;
}


bool CInput::ReadBufferedMouse(void)
{
	HRESULT result;	//	For Error Tracking.

	//	Make sure the Mouse Device was made.
	if (!m_lpDIMouse)	return false;

	if (FAILED(m_lpDIMouse->Poll()))
		m_lpDIMouse->Acquire();

	m_dwMouseElements = BUFFER_SIZE;
	memset(&m_didodMouse, 0, sizeof(m_didodMouse));
	//	Keep attempting to acquire the Mouse.
	if (result = m_lpDIMouse->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), m_didodMouse, &m_dwMouseElements, 0) == DIERR_INPUTLOST)
	{
		//	Could not re-acquire the Mouse.
		if (FAILED(result = m_lpDIMouse->Acquire()))
			return false;
	}

	if (m_dwMouseElements > 0)
		return true;

	//	Return success.
	return true;
}

bool CInput::OnMouseButtonRelease(BYTE mButton)
{
	//	Check to see if the button was pressed and set a flag.
	if (GetMouseButton(mButton))
	{
		if (!m_bMouseButtons[mButton])
			m_bMouseButtons[mButton] = true;
	}
	//	Checks to see if the button isn't pressed and if the flag is true then
	//	go ahead and change the flag and return true.
	else if (!GetMouseButton(mButton))
	{
		if (m_bMouseButtons[mButton])
		{
			m_bMouseButtons[mButton] = false;
			//	The Mouse Button was release.
			return true;
		}
	}

	//	Mouse button was not released.
	return false;
}

bool CInput::GetMouseButton(BYTE mButton)
{
	//	Check to see if the Mouse Button was pressed.
	if (m_diMouseState.rgbButtons[mButton] & 0x80)
		return true;

	//	Button Not Pressed.
	return false;
}


bool CInput::GetBufferedMouseButton(BYTE mButton)
{
	//	Go through each element in the Buffer.
	for (DWORD i = 0; i < m_dwMouseElements; i++)
	{
		//	If the Key is pressed return success.  Add 12 because DirectInput uses 
		//	DIMOFS_BUTTON0 and so forth for the check on this.
		if ((m_didodMouse[i].dwOfs == (DWORD)(mButton + 12)) && (m_didodMouse[i].dwData & 0x80))
			return true;
	}

	//	Return Failure.
	return false;
}


LONG CInput::GetMouseAxis(LONG lAxis)
{
	//	Check the Axis and return its state.
	switch (lAxis)
	{
	case XAXIS:
		return m_diMouseState.lX;
		break;

	case YAXIS:
		return m_diMouseState.lY;
		break;

	case ZAXIS:
		return m_diMouseState.lZ;
		break;
	};

	return 0;
}

